import { ecs } from "./ECS";
import { ECSMask } from "./ECSMask";
import { CompCtor, CompType, ECSModel } from "./ECSModel";


function createComp<T extends ecs.IComp>(ctor:CompCtor<T>):T{
    const cct = ECSModel.compCtors[ctor.tid];
    if(!cct){
        throw Error(`没有找到该组件的构造函数，检查${ctor.compName}是否为不可构造的组件`);
    }
    let comps = ECSModel.compPools.get(ctor.tid)!;
    let component = comps.pop() || new (cct as CompCtor<T>);
    return component as T;
}

function BroadcastCompAddOrRemove(entity:ECSEntity,componentTypeId:number){

}


function destroyEntity(entity:ECSEntity){
    if (ECSModel.eid2Entity.has(entity.eid)) {
        let entitys = ECSModel.entityPool.get(entity.name);
        if (entitys==null) {
            entitys = []
            ECSModel.entityPool.set(entity.name,entitys);
        }
        entitys.push(entity);
        ECSModel.eid2Entity.delete(entity.eid);
    }else{
        console.warn('试图销毁不存在的实体');
    }
}

export class ECSEntity{
    /**实体类唯一标识 */
    eid:number = -1;
    /** 实体对象名 */
    name:string ="";
    /**实体是否有效果 */
    isValid:boolean = true;
    /** 组件过滤数据 */
    private mask = new ECSMask();

    /**当前实体身上附加的组件构造函数 */
    private compTid2Ctor:Map<number,CompType<ecs.IComp>> = new Map();
    /** 配合 entity.remove(Comp, false)， 记录组件实例上的缓存数据，在添加时恢复原数据 */
    private compTid2Ojb:Map<number,ecs.IComp> = new Map();

    private _parent:ECSEntity | null  = null;

    private _children:Map<number,ECSEntity> | null  = null;

    get parent(){
        return this.parent;
    }

    set parent(value:ECSEntity | null){
        this._parent = value;
    }
    
    get children():Map<number,ECSEntity>{
        if (this._children==null) {
            this._children = new Map<number,ECSEntity | null>();
        }
        return this._children;
    }

    /**
     * 添加子实体
     * @param entity 
     */
    public addChild(entity:ECSEntity):void{
        entity._parent = this;
        this.children.set(entity.eid,entity);
    }

    /**
     * 移除子实体
     * @param entity 
     * @param isDestroy 
     */
    public removeChild(entity:ECSEntity,isDestroy:boolean=false):void{
        if (this.children==null) {
            return;
        }
        entity.parent = null;
        entity.children.delete(entity.eid);
        if (isDestroy) {
            entity.destroy();
        }
    }
    
    // public add<T extends ecs.IComp>(obj:T):ECSEntity;
    // public add<T extends ecs.IComp>(ctor:CompType<T>,isReAdd?:boolean):boolean;
    // public add<T extends ecs.IComp>(ctor:CompType<T> | T,isReAdd:boolean=false):T | ECSEntity{
        add<T extends ecs.IComp>(obj: T): ECSEntity;
        add<T extends ecs.IComp>(ctor: CompType<T>, isReAdd?: boolean): T;
        add<T extends ecs.IComp>(ctor: CompType<T> | T, isReAdd: boolean = false): T | ECSEntity {
        if (typeof ctor  === "function") {
            let compTid = ctor.tid;
            if (ctor.tid === -1) {
                throw Error(`[${this.name}]实体[${ctor.compName}]组件未注册`)
            }
            if(this.compTid2Ojb.has(compTid)){
                if (isReAdd) {
                    this.remove(ctor)
                }else{
                    console.log(`【${this.name}】实体【${ctor.compName}】组件已存在`);
                    // TODO:这里应该是@ts-igone 但是这里不显示，所以有问题
                    return this[ctor.compName] as T;
                }
            }
            this.mask.set(compTid);
            let comp:T;
            if (this.compTid2Ctor.has(compTid)) {
                comp = this.compTid2Ojb.get(compTid) as T;
            }else{
                comp = createComp(ctor) as T;
            }
            // 将组件对象直接附加到实体对象身上，方便直接获取
            this[ctor.compName] = comp;
            this.compTid2Ctor.set(compTid,ctor);
            comp.ent = this;
            BroadcastCompAddOrRemove(this,compTid);
            return comp;
        }else{
            let tmpCtor = (ctor.constructor as CompCtor<T>);
            let compTid = tmpCtor.tid;
            if (compTid===-1 || compTid===null) {
                throw Error(`[${this.name}实体]【${tmpCtor.name}】组件未注册`)
            }
            if (this.compTid2Ctor.has(compTid)) {
                throw Error(`[${this.name}]实体${tmpCtor.name}组件已存在`)
            }
            this.mask.set(compTid);
            // TODO:这里应该是@ts-igone 但是这里不显示，所以有问题
            this[tmpCtor.compName] = ctor;
            this.compTid2Ctor.set(compTid,tmpCtor);
            //@ts-ignore
            ctor.ent = this;
            //@ts-ignore
            ctor.canRecycle = false;
            BroadcastCompAddOrRemove(this,compTid);
            return this;
        }
    }

    /**
     * 批量添加组件
     * @param ctors 
     * @returns 
     */
    public addComponents<T extends ecs.IComp>(...ctors:CompType<T>[]){
        for(let ctor of ctors){
            this.add(ctor);
        }
        return this;
    }
     
    public get(ctor:number):number;
    public get<T extends ecs.IComp>(ctor:CompCtor<T>):T
    public get<T extends ecs.IComp>(ctor:CompCtor<T> | number):T{
        //@ts-ignore
        return this[ctor.compName];
    }

    public has(ctor:CompType<ecs.IComp>):boolean{
        if (typeof ctor === "number") {
            return this.mask.has(ctor)
        }else{
            return this.compTid2Ctor.has(ctor.tid);
        }
    }

 
    /**
     *  从实体上删除指定组件
     * @param ctor 
     * @param isRecycle 
     */
    public remove(ctor:CompType<ecs.IComp>,isRecycle:boolean = false):void{
        let hasComp = false;
        //@ts-ignore
        let componentTypeId = ctor.tid;
        //@ts-ignore
        let compName = ctor.compName;

        if (this.mask.has(componentTypeId)) {
            hasComp = true;
            //@ts-ignore
            let comp = this[ctor.compName];

            comp.ent = null;

            if (comp.canRecycle) {
                comp.reset();
                if (comp.canRecycle) {
                    const compPoolsType = ECSModel.compPools.get(componentTypeId);
                    compPoolsType.push(comp);
                }
            }else{
                this.compTid2Ojb.set(componentTypeId,comp)
            }

        }

        if (hasComp) {
            //TODO:这里也应该是报红，但是没有报，查看
            //@ts-ignore
            this[compName] = null;
            this.mask.delete(componentTypeId);
            this.compTid2Ojb.delete(componentTypeId);
            BroadcastCompAddOrRemove(this,componentTypeId);
        }

    }

    public destroy(){
        this.isValid = false;

        if(this._parent){
            this._parent.removeChild(this,false);
            this._parent = null;
        }

        if (this._children) {
            this._children.forEach(e=>{
                this.removeChild(e);
            })
            this._children = null;
        }
        this.compTid2Ctor.forEach(this._remove,this);
        destroyEntity(this);
        this.compTid2Ojb.clear();
    }

    private _remove(comp:CompType<ecs.IComp>){
        this.remove(comp,true);
    }

}